#include "Windows7VirtualPrinterCreator.h"
#include "IVirtualPrinterDriverInstaller.h"
#include "IVirtualPortMonitorInstaller.h"

extern void AppendErrorLogToFile(const char* log);
extern void AppendInfoLogToFile(const char* log);

typedef struct {
	LPWSTR   pszMethod;
	DWORD(*pfn)	(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg);
	LPWSTR* szArg;
}	PRINTER_ATTRIBUTE_INIT_METHOD, * PPRINTER_ATTRIBUTE_INIT_METHOD;

DWORD CalcDupStrNeedMallocSize(LPWSTR str);

DWORD PrinterAttributeMonitorPortNameInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg);
DWORD PrinterAttributeDriverNameAttributeInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg);
DWORD PrinterAttributePrinterNameInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg);
DWORD PrinterAttributePrintProcessorInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg);
DWORD PrinterAttributePrinterDatatypeInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg);

PRINTER_ATTRIBUTE_INIT_METHOD  PrinterAttributeInitMethod[] = {
						{(LPWSTR)L"PrinterAttributeMonitorPortNameInit", PrinterAttributeMonitorPortNameInit, &IVirtualPortMonitorInstaller::VritualPortMonitorPortName},
						{(LPWSTR)L"PrinterAttributePrintProcessorInit", PrinterAttributePrintProcessorInit, &Windows7VirtualPrinterCreator::VirtualPrintProcessName},
						{(LPWSTR)L"PrinterAttributePrinterDatatypeInit", PrinterAttributePrinterDatatypeInit, &IVirtualPrinterDriverInstaller::DriverPrinterDefaultDataTypeName},
						{NULL, NULL}
};

Windows7VirtualPrinterCreator::Windows7VirtualPrinterCreator()
{

}

Windows7VirtualPrinterCreator::~Windows7VirtualPrinterCreator()
{

}

bool Windows7VirtualPrinterCreator::AddNewCommonVirtualPrinter(LPWSTR wszDriverName, LPWSTR wszPrinterName)
{
	HANDLE hPrinter = NULL;

	PRINTER_INFO_2W* pPrinterInfoW = GenerateVirtualPrinterInstallInfo(wszDriverName, wszPrinterName);
	if (pPrinterInfoW != NULL) {
		hPrinter = AddPrinterW(NULL, 2, (LPBYTE)pPrinterInfoW);
		destroyVirtualPrinterInstallInfo(pPrinterInfoW);
		pPrinterInfoW = NULL;

		AppendErrorLogToFile("AddNewCommonVirtualPrinter(): GenerateVirtualPrinterInstallInfo Error");
	}

	if (hPrinter != NULL) {
		ClosePrinter(hPrinter);
	}

	return hPrinter == NULL ? FALSE : TRUE;
}

PRINTER_INFO_2W* Windows7VirtualPrinterCreator::GenerateVirtualPrinterInstallInfo(LPWSTR wszDriverName, LPWSTR wszPrinterName)
{
	PRINTER_INFO_2W* pPrinterInfoW = (PRINTER_INFO_2W*)malloc(sizeof(PRINTER_INFO_2W));
	if (pPrinterInfoW != NULL) {
		do {
			memset(pPrinterInfoW, 0, sizeof(PRINTER_INFO_2W));

			pPrinterInfoW->DefaultPriority = 1;
			pPrinterInfoW->pServerName = NULL;
			pPrinterInfoW->Attributes = 0x00000002;

			if (PrinterAttributeDriverNameAttributeInit(pPrinterInfoW, wszDriverName) < 0) {
				destroyVirtualPrinterInstallInfo(pPrinterInfoW);
				pPrinterInfoW = NULL;

				AppendErrorLogToFile("GenerateVirtualPrinterInstallInfo(): PrinterAttributeDriverNameAttributeInit Error");
				break;
			}

			if (PrinterAttributePrinterNameInit(pPrinterInfoW, wszPrinterName) < 0) {
				destroyVirtualPrinterInstallInfo(pPrinterInfoW);
				pPrinterInfoW = NULL;

				AppendErrorLogToFile("GenerateVirtualPrinterInstallInfo(): PrinterAttributePrinterNameInit Error");
				break;
			}

			for (DWORD dwEnumIndex = 0; PrinterAttributeInitMethod[dwEnumIndex].pfn != NULL; dwEnumIndex++) {
				if (PrinterAttributeInitMethod[dwEnumIndex].pfn(pPrinterInfoW, *PrinterAttributeInitMethod[dwEnumIndex].szArg) < 0) {
					destroyVirtualPrinterInstallInfo(pPrinterInfoW);
					pPrinterInfoW = NULL;

					AppendErrorLogToFile("GenerateVirtualPrinterInstallInfo(): PrinterAttributeInitMethod->pfn Error Ret < 0");
					break;
				}
			}
		} while (false);

	}

	return pPrinterInfoW;
}

DWORD PrinterAttributeMonitorPortNameInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg) {
	DWORD dwNeedAllocSize = CalcDupStrNeedMallocSize(szArg);

	pPrinterInfoW->pPortName = (LPWSTR)malloc(dwNeedAllocSize);
	if (pPrinterInfoW->pPortName != NULL) {
		DWORD dwNeedCopyCount = (dwNeedAllocSize / sizeof(WCHAR)) - 1;
		memset(pPrinterInfoW->pPortName, 0, dwNeedAllocSize);
		wcsncpy(pPrinterInfoW->pPortName, szArg, dwNeedCopyCount);
		dwNeedCopyCount -= wcslen(szArg);
	}
	else {
		AppendErrorLogToFile("PrinterAttributeMonitorPortNameInit(): Malloc Memory Error");
	}

	return pPrinterInfoW->pPortName == NULL ? -1 : 1;
}

DWORD PrinterAttributeDriverNameAttributeInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg) {
	DWORD dwNeedAllocSize = CalcDupStrNeedMallocSize(szArg);

	pPrinterInfoW->pDriverName = (LPWSTR)malloc(dwNeedAllocSize);
	if (pPrinterInfoW->pDriverName != NULL) {
		DWORD dwNeedCopyCount = (dwNeedAllocSize / sizeof(WCHAR)) - 1;
		memset(pPrinterInfoW->pDriverName, 0, dwNeedAllocSize);
		wcsncpy(pPrinterInfoW->pDriverName, szArg, dwNeedCopyCount);
		dwNeedCopyCount -= wcslen(szArg);
	}
	else {
		AppendErrorLogToFile("PrinterAttributeDriverNameAttributeInit(): Malloc Memory Error");
	}

	return pPrinterInfoW->pDriverName == NULL ? -1 : 1;
}

DWORD PrinterAttributePrinterNameInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg) {
	DWORD dwNeedAllocSize = CalcDupStrNeedMallocSize(szArg);

	pPrinterInfoW->pPrinterName = (LPWSTR)malloc(dwNeedAllocSize);
	if (pPrinterInfoW->pPrinterName != NULL) {
		DWORD dwNeedCopyCount = (dwNeedAllocSize / sizeof(WCHAR)) - 1;
		memset(pPrinterInfoW->pPrinterName, 0, dwNeedAllocSize);
		wcsncpy(pPrinterInfoW->pPrinterName, szArg, dwNeedCopyCount);
		dwNeedCopyCount -= wcslen(szArg);
	}
	else {
		AppendErrorLogToFile("PrinterAttributePrinterNameInit(): Malloc Memory Error");
	}

	return pPrinterInfoW->pPrinterName == NULL ? -1 : 1;
}

DWORD PrinterAttributePrintProcessorInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg) {
	DWORD dwNeedAllocSize = CalcDupStrNeedMallocSize(szArg);

	pPrinterInfoW->pPrintProcessor = (LPWSTR)malloc(dwNeedAllocSize);
	if (pPrinterInfoW->pPrintProcessor != NULL) {
		DWORD dwNeedCopyCount = (dwNeedAllocSize / sizeof(WCHAR)) - 1;
		memset(pPrinterInfoW->pPrintProcessor, 0, dwNeedAllocSize);
		wcsncpy(pPrinterInfoW->pPrintProcessor, szArg, dwNeedCopyCount);
		dwNeedCopyCount -= wcslen(szArg);
	}
	else {
		AppendErrorLogToFile("PrinterAttributePrintProcessorInit(): Malloc Memory Error");
	}

	return pPrinterInfoW->pPrintProcessor == NULL ? -1 : 1;
}

DWORD PrinterAttributePrinterDatatypeInit(PRINTER_INFO_2W* pPrinterInfoW, LPWSTR szArg) {
	DWORD dwNeedAllocSize = CalcDupStrNeedMallocSize(szArg);

	pPrinterInfoW->pDatatype = (LPWSTR)malloc(dwNeedAllocSize);
	if (pPrinterInfoW->pDatatype != NULL) {
		DWORD dwNeedCopyCount = (dwNeedAllocSize / sizeof(WCHAR)) - 1;
		memset(pPrinterInfoW->pDatatype, 0, dwNeedAllocSize);
		wcsncpy(pPrinterInfoW->pDatatype, szArg, dwNeedCopyCount);
		dwNeedCopyCount -= wcslen(szArg);
	}
	else {
		AppendErrorLogToFile("PrinterAttributePrinterDatatypeInit(): Malloc Memory Error");
	}

	return pPrinterInfoW->pDatatype == NULL ? -1 : 1;
}

DWORD CalcDupStrNeedMallocSize(LPWSTR szDupStr) {
	DWORD dwDupNeedBytesSize = 0;

	if (szDupStr != NULL) {
		dwDupNeedBytesSize = (wcslen(szDupStr) + sizeof(char)) * sizeof(WCHAR);
	}

	return dwDupNeedBytesSize;
}

void Windows7VirtualPrinterCreator::destroyVirtualPrinterInstallInfo(PRINTER_INFO_2W* info)
{
	if (info == NULL) {
		return;
	}

	if (info->pPortName != NULL) {
		free(info->pPortName);
		info->pPortName = NULL;
	}

	if (info->pDriverName != NULL) {
		free(info->pDriverName);
		info->pDriverName = NULL;
	}

	if (info->pDatatype != NULL) {
		free(info->pDatatype);
		info->pDatatype = NULL;
	}

	if (info->pPrinterName != NULL) {
		free(info->pPrinterName);
		info->pPrinterName = NULL;
	}

	if (info->pPrintProcessor != NULL) {
		free(info->pPrintProcessor);
		info->pPrintProcessor = NULL;
	}

	free(info);
	info = NULL;
}